/*******************************************************************************
 * Copyright (c) 2019, 2020 Johannes Kepler University Linz
 *
 * This program and the accompanying materials are made available under the
 * terms of the Eclipse Public License 2.0 which is available at
 * http://www.eclipse.org/legal/epl-2.0.
 *
 * SPDX-License-Identifier: EPL-2.0
 *
 * Alois Zoitl - initial implementation
 * Bianca Wiesmayr - enhanced add functionality
 * Daniel Lindhuber - added separate delete listener
 *******************************************************************************/
package org.eclipse.fordiac.ide.ui.widget;

import java.util.List;

import org.eclipse.fordiac.ide.ui.providers.AbstractCreationCommand;
import org.eclipse.fordiac.ide.ui.providers.CommandProvider;
import org.eclipse.fordiac.ide.ui.providers.CreationCommandProvider;
import org.eclipse.gef.commands.CompoundCommand;
import org.eclipse.jface.viewers.StructuredSelection;
import org.eclipse.jface.viewers.TableViewer;
import org.eclipse.swt.SWT;
import org.eclipse.swt.events.KeyEvent;
import org.eclipse.swt.events.KeyListener;
import org.eclipse.swt.layout.FillLayout;
import org.eclipse.swt.layout.GridData;
import org.eclipse.swt.widgets.Button;
import org.eclipse.swt.widgets.Composite;
import org.eclipse.swt.widgets.Listener;
import org.eclipse.ui.ISharedImages;
import org.eclipse.ui.PlatformUI;
import org.eclipse.ui.forms.widgets.FormToolkit;

public class AddDeleteWidget {
	private Button createButton;
	private Button deleteButton;
	private Composite container;

	public void createControls(Composite parent, FormToolkit widgetFactory) {
		container = createContainer(parent);

		createAddButton(widgetFactory, container);

		createDeleteButton(widgetFactory, container);

		// initially nothing should be selected therefore deactivate the buttons
		setButtonEnablement(false);
	}

	public void setVisible(boolean visible) {
		container.setVisible(visible);
		((GridData) container.getLayoutData()).exclude = !visible;
		container.getParent().pack();
	}

	protected void createDeleteButton(FormToolkit widgetFactory, Composite container) {
		deleteButton = widgetFactory.createButton(container, "", SWT.PUSH); //$NON-NLS-1$
		deleteButton.setToolTipText("Delete selected interface element"); //$NON-NLS-1$
		deleteButton.setImage(PlatformUI.getWorkbench().getSharedImages().getImage(ISharedImages.IMG_TOOL_DELETE));
	}

	protected void createAddButton(FormToolkit widgetFactory, Composite container) {
		createButton = widgetFactory.createButton(container, "", SWT.PUSH); //$NON-NLS-1$
		createButton.setImage(PlatformUI.getWorkbench().getSharedImages().getImage(ISharedImages.IMG_OBJ_ADD));
		createButton.setToolTipText("Create element");
	}

	protected static Composite createContainer(Composite parent) {
		Composite container = new Composite(parent, SWT.NONE);
		GridData buttonCompLayoutData = new GridData(SWT.CENTER, SWT.TOP, false, false);
		container.setLayoutData(buttonCompLayoutData);
		container.setLayout(new FillLayout(SWT.VERTICAL));
		return container;
	}

	public void setButtonEnablement(boolean enable) {
		deleteButton.setEnabled(enable);
		deleteButton
				.setImage((enable) ? PlatformUI.getWorkbench().getSharedImages().getImage(ISharedImages.IMG_TOOL_DELETE)
						: PlatformUI.getWorkbench().getSharedImages().getImage(ISharedImages.IMG_TOOL_DELETE_DISABLED));
	}

	public void setCreateButtonEnablement(boolean enable) {
		createButton.setEnabled(enable);
	}

	public void addCreateListener(Listener createListener) {
		createButton.addListener(SWT.Selection, createListener);
	}

	public void addDeleteListener(Listener deleteListener) {
		deleteButton.addListener(SWT.Selection, deleteListener);
	}

	public void bindToTableViewer(TableViewer viewer, CommandExecutor executor, CreationCommandProvider addCommand,
			CommandProvider deleteCommand) {

		Listener createListener = getAddListener(viewer, executor, addCommand);

		Listener deleteListener = getDeleteListener(viewer, executor, deleteCommand);

		bindToTableViewer(viewer, createListener, deleteListener);
	}

	public void bindToTableViewer(TableViewer viewer, Listener createListener, Listener deleteListener) {

		addCreateListener(createListener);
		addDeleteListener(deleteListener);

		viewer.addSelectionChangedListener(ev -> setButtonEnablement(!viewer.getSelection().isEmpty()));

		viewer.getTable().addKeyListener(new KeyListener() {

			@Override
			public void keyReleased(KeyEvent e) {
				// Nothing to do here
			}

			@Override
			public void keyPressed(KeyEvent e) {
				if ((e.keyCode == SWT.INSERT) && (e.stateMask == 0)) {
					createListener.handleEvent(null);
				} else if ((e.character == SWT.DEL) && (e.stateMask == 0)) {
					deleteListener.handleEvent(null);
				}
			}
		});
	}

	@SuppressWarnings("unchecked")
	public static Listener getSelectionListener(TableViewer viewer, CommandExecutor executor,
			CommandProvider commandProvider) {
		return ev -> {
			if (!viewer.getStructuredSelection().isEmpty()) {
				executeCompoundCommandForList(viewer, viewer.getStructuredSelection().toList(), executor,
						commandProvider);
			}
		};
	}

	protected static void executeCompoundCommandForList(TableViewer viewer, List<Object> selection,
			CommandExecutor executor, CommandProvider commandProvider) {
		CompoundCommand cmd = new CompoundCommand();
		selection.stream().forEach(elem -> cmd.add(commandProvider.getCommand(elem)));
		executor.executeCommand(cmd);
		viewer.refresh();
	}

	@SuppressWarnings("unchecked")
	public static Listener getDeleteListener(TableViewer viewer, CommandExecutor executor,
			CommandProvider commandProvider) {
		return ev -> {
			if (!viewer.getStructuredSelection().isEmpty()) {
				int pos = viewer.getTable().getSelectionIndices()[0];
				executeCompoundCommandForList(viewer, viewer.getStructuredSelection().toList(), executor,
						commandProvider);
				int itemCnt = viewer.getTable().getItemCount();
				if (pos <= itemCnt) {
					if (pos == itemCnt) {
						pos--;
					}
					viewer.getTable().forceFocus();
					// the selection has to be set again via the table viewer for the widgets to
					// recognize it
					viewer.getTable().setSelection(pos);
					viewer.setSelection(viewer.getSelection());
				}
			}
		};
	}

	private static Listener getAddListener(TableViewer viewer, CommandExecutor executor,
			CreationCommandProvider commandProvider) {
		return ev -> {
			AbstractCreationCommand cmd = commandProvider.getCommand(getReferencedElement(viewer));
			executor.executeCommand(cmd);
			viewer.refresh();
			viewer.setSelection(new StructuredSelection(cmd.getCreatedElement()));
		};
	}

	private static Object getReferencedElement(TableViewer viewer) {
		if (!viewer.getStructuredSelection().isEmpty()) {
			return viewer.getStructuredSelection().toList().get(viewer.getStructuredSelection().size() - 1);
		}
		return null;
	}
}
